package cn.agiledata.bank.transaction.action;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import cn.agiledata.bank.common.dao.AccountTemplateInter;
import cn.agiledata.bank.common.dao.ActionTypeTemplate;
import cn.agiledata.bank.common.dao.TransactionTemplate;
import cn.agiledata.bank.common.service.ICommonService;
import cn.agiledata.bank.common.service.PermissionService;
import cn.agiledata.bank.common.util.Constant;
import cn.agiledata.bank.common.util.ForeDict;
import cn.agiledata.bank.common.util.UniqueIdentityUtil;
import cn.agiledata.bank.common.util.WeatherUtil;
import cn.agiledata.bank.hibernateBean.Account;
import cn.agiledata.bank.hibernateBean.AccountLimit;
import cn.agiledata.bank.hibernateBean.ActionType;
import cn.agiledata.bank.hibernateBean.PeerInnerAccount;
import cn.agiledata.bank.hibernateBean.SubAccount;
import cn.agiledata.bank.hibernateBean.Transaction;
import cn.agiledata.bank.myspace.service.IMySpaceService;
import cn.agiledata.bank.transaction.audit.service.EsPrePayTransService;
import cn.agiledata.bank.transaction.exception.TransactionException;
import cn.agiledata.bank.transaction.form.InnerTransactForm;
import cn.agiledata.bank.transaction.form.MultiStepForm;
import cn.agiledata.bank.transaction.service.FeeRatioInterf;
import cn.agiledata.bank.transaction.service.TransactionService;
import com.vnv.infrastructure.action.IPBBaseAction;
import com.vnv.infrastructure.logic.IPBAppFactory;
import com.vnv.infrastructure.logic.IPBMgrFactory;

/**
 * 账户间交易Action
 * 
 * @author maxianbo
 * 
 */
public abstract class InnerTransactAction extends IPBBaseAction {
    /**
     * 分发调度
     */
    public final ActionForward assign(ActionMapping mapping,
            ActionForm actionForm, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        MultiStepForm form = (MultiStepForm) actionForm;
        int step = form.getStep();
        ActionForward forward;
        switch (step) {
        case 0: // 第0步准备初始化数据
            forward = prepare(mapping, actionForm, request, response);
            break;
        case 1: // 第1步填写账户间交易信息
            forward = fill(mapping, actionForm, request, response);
            break;
        case 2: // 第2步进行账户间交易操作
            forward = transact(mapping, actionForm, request, response);
            break;
        default: // 其它步骤序数跳转到第0步，进入新的账户间交易流程，防止错误的步骤序数被提交
            form.setStep(0);
            forward = prepare(mapping, actionForm, request, response);
        }
        // 设置新的步骤序数，最大步骤序数为2
        form.setNextStep(2);
        return forward;
    }

    protected final AccountTemplateInter getAccountTemplate() {
        return (AccountTemplateInter) IPBAppFactory
                .getObj(ForeDict.ACCOUNT_TEMPLATE);
    }
    
    protected final ICommonService getCommonService() {
        return (ICommonService) IPBAppFactory.getObj(ForeDict.SERVICE_COMMON);
    }

    protected final TransactionTemplate getTransactionTemplate() {
        return (TransactionTemplate) IPBAppFactory
                .getObj(ForeDict.TRANSACTION_TEMPLATE);
    }

    protected final IMySpaceService getMySpaceService() {
        return (IMySpaceService) IPBAppFactory
                .getObj(ForeDict.SERVICE_MY_SPACE);
    }

    protected final FeeRatioInterf getFeeRatioService() {
        return (FeeRatioInterf) IPBMgrFactory
                .getMgr(ForeDict.SERVICE_FEE_RATIO);
    }

    protected final EsPrePayTransService getEsPrePayTransService() {
        return (EsPrePayTransService) IPBAppFactory
                .getObj(ForeDict.SERVICE_ES_PRE_PAY_TRANS);
    }

    protected final ActionTypeTemplate getActionTypeTemplate() {
        return (ActionTypeTemplate) IPBAppFactory
                .getObj(ForeDict.ACTION_TYPE_TEMPLATE);
    }

    protected final TransactionService getTransactionService() {
        return (TransactionService) IPBAppFactory
                .getObj(ForeDict.SERVICE_TRANSACTION);
    }

    protected final String getActionType() {
        return Constant.ACTION_TYPE_C; // 交易类型：账户间交易
    }

    /**
     * 根据交易类型值获取交易类型对象
     */
    protected final ActionType getActionType(String actionTypeValue) {
        long actionTypeId = Long.parseLong(actionTypeValue);
        return getActionTypeTemplate().getActionTypeById(actionTypeId);
    }

    /**
     * 获取当前账户
     */
    protected final Account getCurrentAccount(HttpServletRequest request) {
        long accountId = WeatherUtil.getAccountIdFromSession(request);
        return getAccountTemplate().getAccount(String.valueOf(accountId));
    }

    /**
     * 判断当前用户是否个人用户
     */
    protected final boolean isPersonAccount(HttpServletRequest request) {
        return isPersonAccount(getCurrentAccount(request));
    }

    /**
     * 判断指定用户是否个人用户
     */
    protected final boolean isPersonAccount(Account account) {
        return Constant.ACCOUNT_TYPE_BUYER.equals(account.getAccountType());
    }

    /**
     * 校验金额的格式和数值
     * 
     * @param amountValue
     *            字符串型金额值
     * @return Double型金额值
     * @throws TransactionException
     *             如果金额格式错误或金额值小于等于0
     */
    protected Double validateAmount(String amountValue)
            throws TransactionException {
        try {
            Double amount = new Double(amountValue);
            if (amount.doubleValue() <= 0) {
                throw TransactionException.INVALID_AMOUT;
            }
            return amount;
        } catch (NumberFormatException e) {
            throw TransactionException.INVALID_AMOUT;
        }
    }

    /**
     * 获取此交易付款方的手续费
     * 
     * @param subAccount
     *            付款子账户
     * @param amount
     *            申请付款金额
     * @return 手续费
     */
    protected final double getDebitFee(SubAccount debitSubAccount, Double amount) {
        double debitFee = getFeeRatioService().getDebitFee(debitSubAccount,
                getActionType(), amount, null);
        return debitFee;
    }

    /**
     * 获取此交易收款方的手续费
     * 
     * @param subAccount
     *            收款子账户
     * @param amount
     *            申请收款金额
     * @return 手续费
     */
    protected final double getCreditFee(Account creditAccount, Double amount) {
        double creditFee = getFeeRatioService().getCreditFee(null,
                creditAccount, getActionType(), amount);
        return creditFee;
    }

    /**
     * 校验付款方子账户余额 备注：付款方手续费系统指定为外扣（即实际从付款方账户扣除的金额为申请付款金额和付款方手续费之和）
     */
    protected final void validateDebitSubAccountBalance(SubAccount subAccount,
            Double amount, double fee) throws TransactionException {
        BigDecimal trueAmount = new BigDecimal(amount.doubleValue());

        // 如果手续费大于0，计算出本次交易需要付款方从账户实际扣除的金额trueAmount
        if (fee > 0) {
            trueAmount = trueAmount.add(new BigDecimal(fee));
        }

        if (getAccountTemplate().compareAccountMoney(
                subAccount.getId().toString(),
                new Double(trueAmount.doubleValue())) != 0) {
            throw TransactionException.LACK_OF_BALANCE;
        }
    }

    /**
     * 校验收款方实际收款金额是否大于0 ---备注：收款方手续费系统指定为内扣（即收款方实际收到的金额为付款方申请付款金额和收款方手续费之差）
     */
    protected final void validateCreditAccountReceiveAmount(Double amount,
            double fee) throws TransactionException {
        BigDecimal transAmount = new BigDecimal(amount.doubleValue());

        // 如果手续费大于0，看收款方实际收款金额是否大于0
        if (fee > 0) {
            if (transAmount.compareTo(new BigDecimal(fee)) <= 0) {
                throw TransactionException.CREDIT_RECEIVE_AMOUT_INVALID;
            }
        }
    }

    /**
     * 校验收款方实际收款金额是否大于0 ---备注：收款方手续费系统指定为内扣（即收款方实际收到的金额为付款方申请付款金额和收款方手续费之差）
     */
    protected final void validateCreditAccountReceiveAmount(
            Account creditAccount, Double amount) throws TransactionException {
        double fee = getCreditFee(creditAccount, amount);
        validateCreditAccountReceiveAmount(amount, fee);
    }

    /**
     * 校验账户金额限制
     */
    protected final void validateAccountLimit(Account account, double amount)
            throws TransactionException {
        if (isPersonAccount(account)) { // 个人用户才进行金额限制
            TransactionTemplate transactionTemplate = getTransactionTemplate();
            AccountLimit accountLimit = transactionTemplate
                    .getAcctlimit(account);
            if (transactionTemplate.ifHigher(account, amount, accountLimit)) {
                throw TransactionException.getAccountLimitException(account,
                        accountLimit, amount);
            }
        }
    }

    /**
     * 加载子账户列表
     */
    protected final void loadSubAccountList(HttpServletRequest request) {
        Account account = getCurrentAccount(request);
        SubAccount defaultSubAccount = account.getDefaultSubAccount();
        if (isPersonAccount(account)) { // 个人用户
            // 获取当前个人用户的所有子账户列表
            Set subAccounts = account.getSubAccounts();
            request.setAttribute("subAccounts", subAccounts);
        } else { // 企业操作员
            // 获取当前企业操作员具有提现权限的子账户列表
            List subAccounts = PermissionService.getPermissionService()
                    .getSubAccounts(request, Constant.PERMIS_TYPE_PAYMONEY,
                            defaultSubAccount);
            request.setAttribute("subAccounts", subAccounts);
        }
    }

    /**
     * 获取付款方子账户
     */
    protected abstract SubAccount getDebitSubAccount(InnerTransactForm form);

    /**
     * 获取收款方子账户
     */
    protected abstract SubAccount getCreditSubAccount(InnerTransactForm form);

    /**
     * 根据是否担保交易设置交易状态
     */
    protected abstract void setTransactionStautsByAssured(Transaction trans,
            boolean assured);

    /**
     * 根据账户间交易数据创建交易对象
     */
    protected Transaction createTransaction(InnerTransactForm innerTransactForm) {
        SubAccount debitSubAccount = getDebitSubAccount(innerTransactForm);
        Transaction trans = null;
        String transId = innerTransactForm.getTransId();
        if (transId != null && transId.length() > 0) {
            trans = getTransactionTemplate().getTransactionByTranId(transId);
        }
        if (trans == null) {
            trans = new Transaction();
            // 新建交易生成交易编号
            trans.setTransId(UniqueIdentityUtil.getTransactionSequence());
            trans.setCreateTime(new Date());
            String createUser = debitSubAccount.getAccount().getId().toString();
            trans.setCreateUser(createUser);
        }
        // 付款方
        trans.setDebit(debitSubAccount);
        // 收款方
        trans.setCredit(getCreditSubAccount(innerTransactForm));
        // 设置交易状态
        setTransactionStautsByAssured(trans, innerTransactForm.isAssured());
        // 交易类型
        trans.setActionType(getActionType(getActionType()));
        // 充值金额
        trans.setAmount(new Double(innerTransactForm.getAmount()));
        // 付款方费率
        double debitFee = getFeeRatioService().getDebitFee(trans);
        trans.setDebitFee(new Double(debitFee));
        // 收款方费率
        double creditFee = getFeeRatioService().getCreditFee(trans);
        trans.setCreditFee(new Double(creditFee));
        // 更新时间和用户
        trans.setUpdateTime(new Date());
        trans.setUpdateUser(debitSubAccount.getAccount().getId().toString());
        // 交易开始时间
        trans.setBeginDate(new Date());
        // 商品信息
        trans.setCommodityInfo(innerTransactForm.getCommodity());
        // 用途
        trans.setPurpose(innerTransactForm.getPurpose());
        // 备注
        trans.setRemark(innerTransactForm.getRemark());
        return trans;
    }

    /**
     * 准备初始化数据
     */
    public abstract ActionForward prepare(ActionMapping mapping,
            ActionForm actionForm, HttpServletRequest request,
            HttpServletResponse response) throws Exception;

    /**
     * 填写账户间交易信息
     */
    public abstract ActionForward fill(ActionMapping mapping,
            ActionForm actionForm, HttpServletRequest request,
            HttpServletResponse response) throws Exception;

    /**
     * 进行账户间交易操作
     */
    public abstract ActionForward transact(ActionMapping mapping,
            ActionForm actionForm, HttpServletRequest request,
            HttpServletResponse response) throws Exception;

    /**
     * 保存交易对方记录
     */
    protected void savePeerInnerAccount(Account account, Account peerAccount) {
        AccountTemplateInter accountTemplate = getAccountTemplate();
        String acctSeq = account.getId().toString();
        if (peerAccount.getAccountType().equals(Constant.ACCOUNT_TYPE_INITIAL)) {
            // 如果交易对方是内部账户，则不保存到预设账户列表里。
            return;
        }
        if (!accountTemplate.ifHasAccountInPeer(peerAccount.getId().toString(),
                acctSeq)) {
            PeerInnerAccount pia = new PeerInnerAccount();
            pia.setAccount(account);
            pia.setPeerAccount(peerAccount);
            // 封装插入日期和修改日期
            WeatherUtil.fillCreateAndUpdateInfo(pia, acctSeq);
            pia.setSource(Constant.PEER_ACCOUNT_SOURCE_TRANSACTED);
            accountTemplate.saveAccountInPeer(pia);
        } else {
            PeerInnerAccount pah = accountTemplate.getPeerAcctHis(acctSeq,
                    peerAccount.getId().toString());
            if (pah != null) {
                pah.setUpdateTime(new Date());
                accountTemplate.saveAccountInPeer(pah);
            }
        }
    }
}
